home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_200 / 262_01 / debug.txt < prev    next >
Text File  |  1988-03-28  |  21KB  |  608 lines

  1. .he /page #
  2. .ce 2
  3. Add a Source Debugger to Your C compiler
  4. by Robert Ramey
  5.  
  6. I really needed a better way of debugging my C programs.  I am
  7. happy with my compiler but it doesn't include a source level
  8. debugger.  Assembler language debuggers are difficult to use
  9. with compiled programs.   After a little thought and some more work
  10. I developed the source debugger you see in this article.  It:
  11.  
  12.  - is written entirely in C (except for 7 assembler language
  13. statements).  Thus it should be transportable to other environments.
  14.  
  15.  - does not requiere that programs to be analyzed be recompiled.
  16. They must be relinked however.
  17.  
  18.  - can trace and display program operation including entry of
  19. functions with display of arguments and return values.
  20.  
  21.  - can set break points at any function entry.
  22.  
  23.  - can display function arguments and global variables using any
  24. convenient format.  Can also display data structures pointed to
  25. by arguments and global variables.
  26.  
  27.  - can be used to analyze programs written in other languages
  28. that use similar argument passing and calling conventions.
  29.  
  30. This debugger cannot display local variables or trace or set
  31. break points which are not function entry points.
  32. Also the debugger can trace only the first level of a
  33. recurrsively called function.
  34. To overcome these limitations would have made the project much
  35. bigger and in many cases would have requiered fiddling with the
  36. compiler.  At least for now,  I decided it wasn't worth it.
  37.  
  38. 1. How to Use the Debugger
  39.  
  40. Use of the debugger will vary somewhat from system to system.  The
  41. description here applies to my computing environment.
  42. This consists of a SB-180 single board computer,
  43. 386k floppy, and a Beehive terminal.  I use CP/M compatible ZRDOS,
  44. ZAS assembler, ZLINK linker and Q/C C compiler.
  45.  
  46. Suppose I want debug the program HELLO which prints a simple
  47. message on the screen and terminates.
  48.  
  49.     #include <stdio>
  50.     main()
  51.     {
  52.         printf("Hello World\n");
  53.     }
  54.  
  55. Normally I link this program with the command:
  56.  
  57.     ZLINK HELLO,CRUNLIB/
  58.  
  59. and execute with the following:
  60.  
  61.     HELLO
  62.  
  63. When I want to analyze and control execution with my debugger I
  64. link with the command:
  65.  
  66.     ZLINK DEBUG,HELLO,CRUNLIB/ $S
  67.  
  68. and execute with:
  69.  
  70.     DEBUG
  71.  
  72. On encountering $S in the link command line, ZLINK generates a symbol
  73. table named DEBUG.SYM for the resulting linked program.  This
  74. symbol table is key to the debugging process.
  75. Before starting execution of the main() function,
  76. the symbol file is read, the debugger
  77. command interpreter takes control and prompts with *.  Now I
  78. can type in debugger commands.
  79.  
  80. 2. Debugger Commands
  81.  
  82. Debugger commands are one letter optionally followed by one or
  83. more arguments.  The command letter can be upper or lower case.
  84.  
  85. 2.1 Display a symbol
  86.  
  87. S [<symbol>] [<hex number>]
  88.  
  89. The S command is to set or display symbols.  S without arguments
  90. displays the whole symbol table.  S with one argument displays
  91. the current value of that symbol.  The value of a symbol is usually
  92. the address of a data item or function entry point.
  93. If a hex number is specified, the symbol is assigned a new value.
  94. If the symbol did not previously exist,  it is created at this
  95. time. Examples:
  96.  
  97.     *S main
  98.     02C2 main
  99.  
  100. If the symbol has been designated as a break point,  the B character
  101. preceeds the value.  Normally this command is used to check which
  102. symbols exist and what their current state is.  Symbols are loaded
  103. from the DEBUG.SYM file at the beginning of program execution so
  104. it is rare that new symbols need be defined.
  105.  
  106. 2.2 Display contents of memory
  107.  
  108. D [<symbol>] [<format string>]
  109.  
  110. The D command is used to display contents of memory.
  111. If D is used with a symbol argument,  the contents of that memory
  112. location are displayed.  Optionally one may specify a format string to
  113. be used to display the memory contents. (see below).
  114. If D is used without arguments, the contents of memory for each
  115. symbol in the data segment are displayed.  Examples:
  116.  
  117.     *D stdin
  118.     stdin=4548
  119.  
  120.     *D stdin input file is %d
  121.     stdin=input file is 17736
  122.  
  123. 2.3 Specify Format String for a Symbol
  124.  
  125. F <symbol> [<format string>]
  126.  
  127. The F command is used to specify the format string to used to display
  128. the contents addressed by the symbol.  This format string is used
  129. by a printf() statement whenever the contents of this memory location
  130. are displayed.  One memory location will be displayed for
  131. each % character in the format string.  If no format string has been
  132. associated with a symbol, a default format string of %x is assumed.
  133. The S command can be used to display a symbols current format string.
  134.  
  135. Format strings are the same ones used by the printf() statement except
  136. that the *, (, and ) characters have a special meaning.  They are used
  137. to permit the formated display of the objects of pointers.
  138. The * character preceeding a % character indicates that the object to
  139. be displayed is pointed to by the contents of memory.
  140. As an example, take the following program fragment:
  141.  
  142.     int a[] = {1, 2, 3,},    /* array */
  143.         *b,        /* pointer to an integer */
  144.         *c;        /* pointer to array */
  145.     ...
  146.     *b = 0;
  147.     c[0] = 4; c[1] = 5; c[2] = 6;
  148.  
  149. appropriate debugger commands might be:
  150.  
  151.     *D a a[0]=%d, a[1]=%d, a[2]=%d
  152.     a a[0]=1, a[1]=2, a[2]=3
  153.  
  154.     *D b &b=%x
  155.     b &b=5F6A
  156.  
  157.     *D b b=*%d
  158.     b 0
  159.  
  160.     *D c *(c[0]=%d, c[1]=%d, c[2]=%d)
  161.     c c[0]=4, c[1]=5, c[2]=6
  162.  
  163. The ( character pushes the next address on to a stack and loads
  164. the pointed to address.  The ) character recovers the saved address.
  165. Parenthesis can be used when the data structures to be displayed
  166. are more complex.  For example,  we might store a series of words
  167. as an array of linked lists.
  168.  
  169.     struct word {
  170.         struct word *next, *prev;
  171.         char    *spelling;
  172.     }
  173.  
  174.     struct word *chain[ARRAYSIZE];
  175.  
  176. The command to display the first three pointers would be:
  177.  
  178.     *D chain %x %x %x
  179.     chain 4556 4578 45A9
  180.  
  181. To display the first structure in each of the first two chains:
  182.  
  183.     *D chain *(nxt=%x, prv=%x, %s), *(nxt=%, prv=%x, %s)
  184.     chain (nxt=5543, prv=554B, martha), (nxt=558B, prv=83BF, anne)
  185.  
  186. To display the first two structures in the first chain:
  187.  
  188.     *D chain *(next=*(%x %x %s) prev=%x %s)
  189.     chain (next=(4384 340D george) prev=89E4 martha)
  190.  
  191. When a symbol corresponds to a function entry point,  the format
  192. string is used to display the arguments when
  193. the function is entered and return value when the function exited.
  194. The first % or pair of parenthesis is used as the format for the
  195. return value while the rest of the format string is used for the
  196. arguments.  For example, if the program contains:
  197.  
  198.     fp = fopen("LST:","r");
  199.  
  200. and the following F debugger command is specified:
  201.  
  202.     *F fopen file pointer=%x filename=%s mode=%s
  203.  
  204. the following will be displayed as the program executes:
  205.  
  206.     >fopen filename=LST: mode=r
  207.     ...
  208.     <fopen=file pointer=457D
  209.  
  210. One final tricky example on the format string:
  211.  
  212.     a[] = "abc";
  213.     b = "abc";
  214.  
  215. a and b cannot be displayed with same format string.  The correct
  216. format strings are:
  217.  
  218.     *D a %c%c%c
  219.     a abc
  220.  
  221.     *D b %s
  222.     b abc
  223.  
  224. This fooled me when I first started using the debugger.
  225. Think about it.
  226.  
  227. The F, D, and B commands can be used to assign a format string to a
  228. symbol.  In all cases the most recently specified format string
  229. becomes the default format string.  The F command can be used
  230. to reinitialize the format string for a symbol to null.
  231.  
  232. 2.4 Set/Reset Trace Mode
  233.  
  234. T
  235.  
  236. Program flow is traced when trace mode is on.  This means that
  237. as each function is entered and exited its name, arguments
  238. and return value are displayed.  In order for arguments to be
  239. displayed a format string must have been previously supplied
  240. for the function.
  241.  
  242. 2.5 Set/Reset break point
  243.  
  244. B [<symbol>] [<format string>]
  245.  
  246. The indicated symbol becomes a break point.
  247. This means that when the function corresponding to the symbol
  248. is entered,  normal execution will be suspended, the
  249. name of the function with its arguments will be displayed,
  250. and the debugger will accept commands from the console.
  251. The break point can be cleared by reissueing the B command.
  252. If the B command is issued without arguments, the B c